/*
 * Copyright 2021-2022 Open Kunlun Technology <https://www.openkunlun.io>
 */

package io.openkunlun.scalarpc.codegen.model

import scala.meta._

/**
 * @author kostas.kougios
 *         Date: 01/09/17
 */
trait MethodEx[A] extends CodeEx
  with CodeEx.Name[MethodEx[A]]
  with MetaEx.Contains
  with MetaEx.ContainsMods[A] {

  override def tree: Stat
  def returnType: Option[TypeEx]
  def withReturnType(returnType: String): MethodEx[A]

  def parameters: Seq[Seq[TermParamEx]]
  def withParameter(param: TermParamEx): MethodEx[A] = withParameters(Seq(Seq(param)))
  def withParameter(src: String): MethodEx[A] = withParameters(Seq(Seq(TermParamEx.fromSource(src))))
  def withParameters(params: Seq[Seq[TermParamEx]]): MethodEx[A]

  // adds impl (or replaces the existing one)
  def implementation: Option[Term]
  def withImplementation(expr: Term): DefinedMethodEx
  def withImplementation(code: String): DefinedMethodEx = withImplementation(code.parse[Term].get)

  def isOverrides: Boolean
  def withOverrides: MethodEx[A]
  // converts this method to it's abstract (no impl) representation

  def isAbstract: Boolean
  def withAbstract: DeclaredMethodEx
}

object MethodEx extends PartialParser[MethodEx[_]] {
  override val parser = DeclaredMethodEx.parser.orElse(DefinedMethodEx.parser)

  def fromSource(s: String): MethodEx[_] = parser(s.parse[Stat].get)

  def isMethod(t: Tree): Boolean = parser.isDefinedAt(t)

  trait Contains[T] extends TemplateEx.Contains[T] {

    def meta: MetaEx with MetaEx.Template
    def methods: Seq[MethodEx[_]] = meta.template.children.collect(parser)
    def withMethods(methods: Seq[MethodEx[_]]): T = {
      withTemplate(
        Template(
          meta.template.early,
          meta.template.inits,
          meta.template.self,
          meta.template.stats.filterNot(isMethod) ++ methods.map(_.tree)
        )
      )
    }

    def declaredMethods: Seq[DeclaredMethodEx] = methods.collect {
      case d: DeclaredMethodEx => d
    }

    def definedMethods: Seq[DefinedMethodEx] = methods.collect {
      case d: DefinedMethodEx => d
    }
  }

}
